/**  
 * All rights Reserved, Designed By <a href="www.itttq.com" target="_blank">www.itttq.com</a>
 * @date:   2017-07-21 11:42:21
 * @Copyright: 2017 <a href="www.itttq.com" target="_blank">www.itttq.com</a> Inc. All rights reserved. 
 */
package com.itttq.gshop.pbpm.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.itttq.gshop.domain.po.OrderInfoPo;
import com.itttq.gshop.domain.po.OrderWarePo;
import com.itttq.gshop.domain.po.UserCartPo;
import com.itttq.gshop.domain.po.UserInfoPo;
import com.itttq.gshop.domain.po.WareInfoPo;
import com.itttq.gshop.domain.to.IndexStatTo;
import com.itttq.gshop.domain.to.OrderTo;
import com.itttq.gshop.domain.to.ResultPageTo;
import com.itttq.gshop.domain.to.ResultTo;
import com.itttq.gshop.generic.util.Constants;
import com.itttq.gshop.generic.util.Constants.OrderOperate;
import com.itttq.gshop.generic.util.Constants.OrderStatus;
import com.itttq.gshop.generic.util.Constants.ScoreChangeCode;
import com.itttq.gshop.generic.util.Constants.Source;
import com.itttq.gshop.pbpm.mapper.IOrderInfoMapper;
import com.itttq.gshop.pbpm.mapper.IOrderOpLogsMapper;
import com.itttq.gshop.pbpm.mapper.IPayApplyMapper;
import com.itttq.gshop.pbpm.mapper.IPayMethodMapper;
import com.itttq.gshop.pbpm.mapper.IUserCartMapper;
import com.itttq.gshop.pbpm.mapper.IWareInfoMapper;
@Service
public class OrderInfoService {
	private static Logger LOG = LoggerFactory.getLogger(OrderInfoService.class);
	@Autowired
	private IOrderInfoMapper orderInfoMapper;
	@Autowired
	private IOrderOpLogsMapper orderOpLogsMapper;
	@Autowired
	private IWareInfoMapper wareInfoMapper;
	@Autowired
	private IUserCartMapper userCartMapper;
	@Autowired
	private IPayApplyMapper payApplyMapper;
	@Autowired
	private IPayMethodMapper payMethodMapper;
	@Autowired
	private OrderOpLogsService orderOpLogsService;
	@Autowired
	private PayApplyService payApplyService;
	@Autowired
	private OrderWareService orderWareService;
	@Autowired
	private UserInfoService userInfoService;
	
	/**
	 * 根据ID获取OrderInfoPo对象
	 * 
	 * @param id
	 * @return
	 * @Author 迷蝶
	 * @return OrderInfoPo
	 */
	public OrderInfoPo getOrderInfo(int id) {
		return orderInfoMapper.findOrderInfoById(id);
	}
	
	/**
	 * 根据条件获取OrderInfoPo对象，并返回查询的数据总数
	 * 
	 * @param orderInfo
	 * @Author 迷蝶
	 * @return ResultPageTo<OrderInfoPo>
	 */
	public ResultPageTo<OrderInfoPo> getOrderInfos(OrderInfoPo orderInfo) {
		return new ResultPageTo<OrderInfoPo>(orderInfo.getStart(), orderInfo.getSize(), 
				orderInfoMapper.findOrderInfoCount(orderInfo), orderInfoMapper.findOrderInfos(orderInfo));
	}
	
	/**
	 * 根据ID删除OrderInfoPo对象
	 * 
	 * @param id
	 * @Author 迷蝶
	 */
	public Integer delOrderInfo(int id) {
		return orderInfoMapper.delOrderInfoById(id);
	}
	
	/**
	 * 增加OrderInfoPo对象
	 * 
	 * @param orderInfo
	 * @Author 迷蝶
	 */
	public Integer addOrderInfo(OrderInfoPo orderInfo) {
		return orderInfoMapper.addOrderInfo(orderInfo);
	}
	
	/**
	 * 根据条件更新OrderInfoPo对象
	 * 
	 * @param orderInfo
	 * @Author 迷蝶
	 */
	public Integer updateOrderInfo(OrderInfoPo orderInfo) {
		return orderInfoMapper.updateOrderInfo(orderInfo);
	}
	
	/**
	 * 根据订单ID更新订单状态，并记录到订单操作记录。<br>
	 * 订单变化限制如下：前面的状态必须由后面的状态变化而来
	 * 待支付1：无<br>
	 * 待发货2：<--待支付1；<br>
	 * 待收货3：<--待发货2；<br>
	 * 已完成4：<--待收货3；<br>
	 * 已取消5：<--待支付1；<br>
	 * 申请退款7：<--待发货2、退货中12；<br>
	 * 退款中8：<--申请退款7；<br>
	 * 已退款9：<--退款中8；<br>
	 * 退款失败10：<--申请退款7；<br>
	 * 申请退货11：<--待收货3；<br>
	 * 退货中12：<--申请退货11；<br>
	 * 退货失败15：<--申请退货11；<br>
	 * 删除16：<--已完成4、已取消5、已退款9、退款失败10、退货失败15<br>
	 * 取消退款：<--申请退款7<br>
	 * 取消退货：<--申请退货11
	 * 
	 * @param id	订单ID
	 * @param status	订单更新后的状态
	 * @param op	操作类型
	 * @param remark	备注
	 * @param operatorId	操作人ID，系统则为0
	 * @param operatorName	操作人名称，系统操作则为：系统
	 * @Author 迷蝶
	 * @return ResultTo
	 */
	@Transactional
	public ResultTo updateOrderStatus(Integer id, String status, String op, String remark, 
			int operatorId, String operatorName) {
		LOG.info("order: update status： orderId = {}, status = {}, op = {}, remark = {}, operator = id:{} , name: {}", 
				id, status, op, remark, operatorId, operatorName);
		ResultTo rt = new ResultTo();
		// 判断null
		if(StringUtils.isBlank(status) || StringUtils.isBlank(op) || StringUtils.isBlank(operatorName) || null == id) {
			rt.setCode(ResultTo.PARAM_INCORRECT);
			return rt;
		}
		OrderStatus os = OrderStatus.getOrderStatusByName(status);
		// TODO 取消订单时还回库存
		if(status.equals(OrderStatus.CANCELED.name())) {
			returnInventory(id);
		}
		int rs = orderInfoMapper.updateOrderStatus(id, os.getVal(), os.getText(), getSourceStatuses(os));
		LOG.info("order: update status result： {}", rs);
		if(rs != 1) {
			rt.setCode(ResultTo.ORDER_STATUS_ERR);
			return rt;
		}
		orderOpLogsService.addOrderOpLogs(id, os, OrderOperate.getOrderOperateByName(op), 
				remark, operatorId, operatorName);
		// 如果是已完成状态，则赠送推广积分
		if(status.equals(OrderStatus.COMPLETED.name())) {
			// 查询订单状态
			OrderInfoPo order = orderInfoMapper.findOrderInfoById(id);
			// 获取用户对象，主要是获取推广人。
			UserInfoPo user = userInfoService.getUserInfo(order.getUserId());
			// 赠送购买积分
			userInfoService.addScore(order.getScore(), user.getId(), ScoreChangeCode.BUY);
			// 赠送推广积分，此处特殊处理为积分只赠送给自己，特定的推广人员
			Integer uid = null;
			if(null != user.getType() && user.getType().intValue() == Constants.TYPE_USER_AMBASSADOR) {
				// 如果是酒坊大使，则推广积分送给自己 
				uid = user.getId();
			} else if(null != user.getPromoterId()) {
				// 如果是普通会员，则推广积分送给推荐自己注册的人
				uid = user.getPromoterId();
			}
			if(null != uid) {
				userInfoService.addScore(order.getScorePromote(), uid, ScoreChangeCode.PROMOTE);
			}
		}
		return rt;
	}
	/**
	 * 根据订单还回库存方法
	 * 
	 * @param orderId
	 * @Author 迷蝶
	 * @return Integer
	 */
	public Integer returnInventory(int orderId) {
		List<OrderWarePo> orderWares = orderWareService.getOrderWareByOrderId(orderId);
		List<WareInfoPo> wares = new ArrayList<WareInfoPo>();
		for(OrderWarePo ow : orderWares) {
			WareInfoPo ware = new WareInfoPo();
			ware.setId(ow.getWareId());
			ware.setNumberInventory(ow.getNumber());
			wares.add(ware);
		}
		// 还回库存
		Map<String, List<WareInfoPo>> waresMap = new HashMap<String, List<WareInfoPo>>();
		waresMap.put(Constants.OP_INVENTORY_ADD, wares);
		return wareInfoMapper.updateInventoryBatch(waresMap);
	}
	/**
	 * 根据目标订单状态，判断应该由那些状态变化而来。<br>
	 * 订单变化限制如下：前面的状态必须由后面的状态变化而来
	 * 待支付1：无<br>
	 * 待发货2：<--待支付1；<br>
	 * 待收货3：<--待发货2；<br>
	 * 已完成4：<--待收货3；<br>
	 * 已取消5：<--待支付1；<br>
	 * 申请退款7：<--待发货2、退货中12；<br>
	 * 退款中8：<--申请退款7；<br>
	 * 已退款9：<--退款中8；<br>
	 * 退款失败10：<--申请退款7；<br>
	 * 申请退货11：<--待收货3；<br>
	 * 退货中12：<--申请退货11；<br>
	 * 退货失败15：<--申请退货11；<br>
	 * 删除16：<--已完成4、已取消5、已退款9、退款失败10、退货失败15、待支付1<br>
	 * 取消退款17：<--申请退款7<br>
	 * 取消退货18：<--申请退货11
	 * 
	 * @param os
	 * @return
	 * @Author 迷蝶
	 * @return String
	 */
	private String getSourceStatuses(OrderStatus os) {
		StringBuffer statusSb = new StringBuffer();
		switch (os) {
			case PAID :
				statusSb.append(OrderStatus.UNPAID.getVal());
				break;
			case SENT :
				statusSb.append(OrderStatus.PAID.getVal());
				break;
			case COMPLETED :
				statusSb.append(OrderStatus.SENT.getVal());
				break;
			case CANCELED :
				statusSb.append(OrderStatus.UNPAID.getVal());
				break;
			case REFUND_APPLY :
				statusSb.append(OrderStatus.PAID.getVal());
				statusSb.append(",");
				statusSb.append(OrderStatus.REFUNDING_GOODS.getVal());
				break;
			case CANCEL_REFUND :
			case REFUNDING :
			case REFUND_FAIL :
				statusSb.append(OrderStatus.REFUND_APPLY.getVal());
				break;
			case REFUNDED :
				statusSb.append(OrderStatus.REFUNDING.getVal());
				break;
			case REFUND_GOODS :
				statusSb.append(OrderStatus.SENT.getVal());
				break;

			case CANCEL_REFUND_GOODS :
			case REFUNDING_GOODS :
			case REFUND_GOODS_FAIL :
				statusSb.append(OrderStatus.REFUND_GOODS.getVal());
				break;
			case DELETED :
				statusSb.append(OrderStatus.COMPLETED.getVal());
				statusSb.append(",");
				statusSb.append(OrderStatus.CANCELED.getVal());
				statusSb.append(",");
				statusSb.append(OrderStatus.REFUNDED.getVal());
				statusSb.append(",");
				statusSb.append(OrderStatus.REFUND_FAIL.getVal());
				statusSb.append(",");
				statusSb.append(OrderStatus.REFUND_GOODS_FAIL.getVal());
				statusSb.append(",");
				statusSb.append(OrderStatus.UNPAID.getVal());
				break;
			default:
				statusSb.append(0); // 此状态值（0）只是表明未知状态，不能修改。实际中没有这个状态
				break;
		}
		return statusSb.toString();
	}
	
	/**
	 * 获取订单全部和当天的统计数据
	 * 
	 * @Author 迷蝶
	 * @return IndexStatTo
	 */
	public IndexStatTo getOrderStat() {
		return orderInfoMapper.findOrderStat();
	}
	
	/**
	 * 创建订单 TODO GXG 事务和方法拆分
	 * 
	 * @param order	订单对象
	 * @Author 迷蝶
	 * @return ResultTo
	 * @throws Exception 
	 */
	@Transactional
	public ResultTo createOrder(OrderInfoPo order) throws Exception {
		ResultTo rt = new ResultTo();
		List<Integer> cartIds = new ArrayList<Integer>();
		List<Integer> wareIds = new ArrayList<Integer>();
		int totalScore = 0;
		int totalScorePromote = 0;
		BigDecimal totalPrice = new BigDecimal(0);
		List<OrderWarePo> orderWares = new ArrayList<OrderWarePo>();
		for(UserCartPo cart : order.getCarts()) {
			cartIds.add(cart.getId());
			wareIds.add(cart.getWareId());
			// 计算订单金额和积分，TODO GXG此处有个BUG，不能从购物车计算积分，需要实时从商品表获取价格和积分
			BigDecimal price = cart.getPricePromotional().multiply(new BigDecimal(cart.getNumber()));
			int score = cart.getScore() * cart.getNumber();
			int scorePromote = cart.getScorePromote() * cart.getNumber();
			totalPrice = price.add(totalPrice);
			totalScore += score;
			totalScorePromote += scorePromote;
			OrderWarePo orderWare = new OrderWarePo();
			orderWare.setAmountTotal(price);
			orderWare.setCreatorId(order.getUserId());
			orderWare.setImageMain(cart.getMainImage());
			orderWare.setNumber(cart.getNumber());
			orderWare.setPriceOriginal(cart.getPriceOriginal());
			orderWare.setPricePromotional(cart.getPricePromotional());
//			orderWare.setRemark(cart.get);
			orderWare.setScore(cart.getScore());
			orderWare.setScoreTotal(score);
			orderWare.setScorePromote(cart.getScorePromote());
			orderWare.setScorePromoteTotal(scorePromote);
			orderWare.setUpdaterId(order.getUserId());
			orderWare.setWareId(cart.getWareId());
			orderWare.setWareName(cart.getWareName());
			orderWares.add(orderWare);
		}

		StringBuffer rsSb = new StringBuffer();
		// 判断库存
		List<WareInfoPo> wares = wareInfoMapper.findWareByIds(wareIds);
		for(WareInfoPo ware : wares) {
			for(UserCartPo cart : order.getCarts()) {
				if(ware.getNumberInventory() > -1 // -1表示不限库存，也为后续可以超卖做预留
						&& cart.getWareId().equals(ware.getId()) 
						&& cart.getNumber().intValue() > ware.getNumberInventory().intValue()) {
					if(rsSb.length() > 0) {
						rsSb.append("，");
					}
					rsSb.append(ware.getName());
				}
				// 设置库存值为本次要扣减的库存量，供批量更新库存使用
				ware.setNumberInventory(cart.getNumber());
			}
		}
		if(rsSb.length() > 0) {
			rt.setCode(ResultTo.ORDER_WARE_NUM);
			rt.setMsg(rsSb.toString());
			return rt;
		}
		// TODO 删除和扣减失败时的处理
		// 删除购物车
		int rs = userCartMapper.delUserCartByIds(cartIds);
		// 扣减库存
		Map<String, List<WareInfoPo>> waresMap = new HashMap<String, List<WareInfoPo>>();
		waresMap.put(Constants.OP_INVENTORY_REDUCT, wares);
		rs = wareInfoMapper.updateInventoryBatch(waresMap);
		// 生成订单
		order.setAmountTotal(totalPrice);
		order.setScore(totalScore);
		order.setScorePromote(totalScorePromote);
		order.setOrderTime(new Date());
		order.setOrderSource(Source.H5.getVal());
		order.setStatus(OrderStatus.UNPAID.getVal());
		order.setStatusName(OrderStatus.UNPAID.getText());
		order.setCode(Constants.getOrderCode(Source.H5.getVal(), order.getUserId())); // 生成订单编码
		rs = orderInfoMapper.addOrderInfo(order);
		if(rs != ResultTo.SUCCESS) {
			rt.setCode(ResultTo.FAIL);
			return rt;
		}
		// 如果有支付方式ID，则调用支付
		if(null != order.getPayMethodId() && order.getPayMethodId() > 0) {
			rt = payApplyService.payApply(order.getId(), order.getPayMethodId());
		}
		// 增加到订单商品记录
		orderWareService.addBatch(orderWares, order.getId());
		// 写入订单操作日志
		orderOpLogsService.addOrderOpLogs(order.getId(), OrderStatus.UNPAID, 
				OrderOperate.ORDER, "生成订单", order.getUserId(), order.getUserName());
		
		// 如果成功，则msg记录orderId返回
		if(rs == ResultTo.SUCCESS) {
			rt.setMsg(String.valueOf(order.getId()));
			return rt;
		}
		return rt;
	}
	
	/**
	 * 获取订单以及订单商品数据，返回分页数据
	 * 
	 * @param order
	 * @Author 迷蝶
	 * @return ResultPageTo<OrderTo>
	 */
	public ResultPageTo<OrderTo> getOrders(OrderInfoPo order) {
		List<OrderTo> orderTos = getOrderWares(order);
		if(null == orderTos || orderTos.isEmpty()) {
			return new ResultPageTo<OrderTo>(order.getStart(), order.getSize(), 0, null);
		}
		return new ResultPageTo<OrderTo>(order.getStart(), order.getSize(), 
				orderInfoMapper.findOrderInfoCount(order), orderTos);
	}
	
	/**
	 * 获取订单以及订单商品数据，没有返回分页数据
	 * 
	 * @param order
	 * @Author 迷蝶
	 * @return List<OrderTo>
	 */
	public List<OrderTo> getOrderWares(OrderInfoPo order) {
		List<OrderInfoPo> orders = orderInfoMapper.findOrderInfos(order);
		if(null == orders || orders.isEmpty()) {
			return null;
		}
		List<Integer> orderIds = new ArrayList<Integer>();
		Map<Integer, OrderTo> orderMap = new HashMap<Integer, OrderTo>();
		for(OrderInfoPo oip : orders) {
			orderIds.add(oip.getId());
			OrderTo ot = new OrderTo();
			BeanUtils.copyProperties(oip, ot);
			orderMap.put(oip.getId(), ot);
		}
		// 获取所有订单的订单商品数据
		List<OrderWarePo> orderWares = orderWareService.getOrderWareByOrderIds(orderIds);
		for(OrderWarePo owp : orderWares) {
			OrderTo ot = orderMap.get(owp.getOrderId());
			List<OrderWarePo> owps = ot.getOrderWares();
			if(null == owps) {
				owps = new ArrayList<OrderWarePo>();
			}
			owps.add(owp);
			ot.setOrderWares(owps);
			orderMap.put(owp.getOrderId(), ot);
		}
		// 设置到orderTo的集合中
		List<OrderTo> orderTos = new ArrayList<OrderTo>();
		for(OrderInfoPo oip : orders) {
			orderTos.add(orderMap.get(oip.getId()));
		}
		return orderTos;
	}
	
	/**
	 * 批量更新订单状态，按订单时间是否大于指定时间来取消。定时任务使用
	 * TODO GXG 有其他状态修改时，修改备注
	 * @param status				更新后的订单状态
	 * @param statusName	状态名称
	 * @param statusCurrent	当前订单状态，更新前，此状态为限制订单状态只能有指定状态更新到下一状态。
	 * @param endTime			指定时间，当下单时间大于此时间的订单才更新
	 * @Author 迷蝶
	 * @return Integer
	 */
	@Transactional
	public Integer updateOrderStatusByTime(int status, int statusCurrent,  Date endTime) {
		OrderStatus os = OrderStatus.getOrderStatusByVal(status);
		int rs = 0;
		List<Integer> ids = orderInfoMapper.findOrderStatusByTime(statusCurrent, endTime);
		if(null != ids && !ids.isEmpty()) {
			rs = orderInfoMapper.updateOrderStatusByTime(status, os.getText(), statusCurrent, ids);
			// 记录订单取消日志 TODO GXG 有其他状态修改时，修改备注
			if(rs > 0 && status == OrderStatus.CANCELED.getVal()) {
				for(int oid : ids) {
					returnInventory(oid);
				}
				orderOpLogsService.addOrderOpLogBatch(ids, os, OrderOperate.CANCELE, 
						"订单超时取消", 0, "系统");
			}
		}
		return rs;
	}
}
